/**
 * @file test_program.cpp
 * @author 心流 (iFlow CLI)
 * @brief 测试OpenCL程序创建、构建和编译功能
 * @version 0.2
 * @date 2025-11-29
 *
 * @copyright Copyright (c) 2025
 *
 */
#define BOOST_TEST_MODULE TestProgram
#include <CL/cl.h>

#include <boost/format.hpp>
#include <boost/test/included/unit_test.hpp>
#include <boost/timer/timer.hpp>
#include <string>
#include <vector>

#include "utility.hpp"
#include "wrapper.hpp"

// 简单的向量加法内核代码，用于测试
const char* simple_kernel_source =
    "__kernel void vector_add(__global const int* a, "
    "                          __global const int* b, "
    "                          __global int* c, "
    "                          const int n) { "
    "    int idx = get_global_id(0); "
    "    if (idx < n) { "
    "        c[idx] = a[idx] + b[idx]; "
    "    } "
    "}";

// 测试使用clCreateProgramWithSource创建程序
BOOST_AUTO_TEST_CASE(TestCreateProgramWithSource)
{
    CLWrapper cl;
    bool      init_result = cl.Init();
    BOOST_CHECK(init_result == true);

    cl_context   context = cl.GetContext();
    cl_device_id device  = cl.GetDeviceID();
    BOOST_CHECK(context != nullptr);
    BOOST_CHECK(device != nullptr);

    // 创建程序对象
    cl_int     err     = CL_SUCCESS;
    cl_program program = clCreateProgramWithSource(
        context, 1, &simple_kernel_source, nullptr, &err);
    BOOST_CHECK_EQUAL(err, CL_SUCCESS);
    BOOST_CHECK(program != nullptr);

    // 构建程序
    err = clBuildProgram(program, 1, &device, nullptr, nullptr, nullptr);
    if (err != CL_SUCCESS)
    {
        // 如果构建失败，获取并打印构建日志
        size_t log_size = 0;
        clGetProgramBuildInfo(
            program, device, CL_PROGRAM_BUILD_LOG, 0, nullptr, &log_size);

        if (log_size > 1)
        {
            std::vector<char> build_log(log_size);
            clGetProgramBuildInfo(program,
                                  device,
                                  CL_PROGRAM_BUILD_LOG,
                                  log_size,
                                  build_log.data(),
                                  nullptr);
            std::cout << "Build log: " << build_log.data() << std::endl;
        }
    }
    BOOST_CHECK_EQUAL(err, CL_SUCCESS);

    // 验证程序对象
    cl_context program_context = nullptr;
    err                        = clGetProgramInfo(program,
                           CL_PROGRAM_CONTEXT,
                           sizeof(program_context),
                           &program_context,
                           nullptr);
    BOOST_CHECK_EQUAL(err, CL_SUCCESS);
    BOOST_CHECK(program_context == context);

    // 获取程序源码（可能获取不到，因为实现可能不保存源码）
    size_t source_size = 0;
    err =
        clGetProgramInfo(program, CL_PROGRAM_SOURCE, 0, nullptr, &source_size);
    if (err == CL_SUCCESS && source_size > 0)
    {
        std::vector<char> source(source_size);
        err = clGetProgramInfo(
            program, CL_PROGRAM_SOURCE, source_size, source.data(), nullptr);
        BOOST_CHECK_EQUAL(err, CL_SUCCESS);
        if (source_size > 1)
        {
            BOOST_CHECK_GT(strlen(source.data()), 0);
        }
    }

    // 清理
    err = clReleaseProgram(program);
    BOOST_CHECK_EQUAL(err, CL_SUCCESS);

    cl.DeInit();
}

// 测试clBuildProgram错误处理和构建日志
BOOST_AUTO_TEST_CASE(TestBuildProgramWithError)
{
    CLWrapper cl;
    bool      init_result = cl.Init();
    BOOST_CHECK(init_result == true);

    cl_context   context = cl.GetContext();
    cl_device_id device  = cl.GetDeviceID();
    BOOST_CHECK(context != nullptr);
    BOOST_CHECK(device != nullptr);

    // 创建包含语法错误的内核源代码
    const char* invalid_kernel_source = R"(
        __kernel void test_kernel(__global int* input, __global int* output) {
            int id = get_global_id(0);
            output[id] = input[id] *;  // 语法错误：缺少操作数
        }
    )";

    // 创建程序对象
    cl_int     err     = CL_SUCCESS;
    cl_program program = clCreateProgramWithSource(
        context, 1, &invalid_kernel_source, nullptr, &err);
    BOOST_CHECK_EQUAL(err, CL_SUCCESS);
    BOOST_CHECK(program != nullptr);

    // 尝试构建程序（应该失败）
    err = clBuildProgram(program, 1, &device, nullptr, nullptr, nullptr);
    // 我们期望这里会失败，所以不检查CL_SUCCESS

    // 获取构建日志
    size_t log_size = 0;
    err             = clGetProgramBuildInfo(
        program, device, CL_PROGRAM_BUILD_LOG, 0, nullptr, &log_size);
    BOOST_CHECK_EQUAL(err, CL_SUCCESS);

    if (log_size > 0)
    {
        std::vector<char> log(log_size);
        err = clGetProgramBuildInfo(program,
                                    device,
                                    CL_PROGRAM_BUILD_LOG,
                                    log_size,
                                    log.data(),
                                    nullptr);
        BOOST_CHECK_EQUAL(err, CL_SUCCESS);
        // 构建日志应该包含错误信息
        BOOST_CHECK_GT(strlen(log.data()), 0);
    }

    // 清理程序
    err = clReleaseProgram(program);
    BOOST_CHECK_EQUAL(err, CL_SUCCESS);

    cl.DeInit();
}

// 测试从二进制创建程序
BOOST_AUTO_TEST_CASE(TestCreateProgramWithBinary)
{
    CLWrapper cl;
    bool      init_result = cl.Init();
    BOOST_CHECK(init_result == true);

    cl_context   context = cl.GetContext();
    cl_device_id device  = cl.GetDeviceID();
    BOOST_CHECK(context != nullptr);
    BOOST_CHECK(device != nullptr);

    // 首先创建一个可执行程序来获取二进制
    const char* kernel_source = R"(
        __kernel void test_kernel(__global int* input, __global int* output) {
            int id = get_global_id(0);
            output[id] = input[id] + 1;
        }
    )";

    // 创建程序对象并构建
    cl_int     err = CL_SUCCESS;
    cl_program temp_program =
        clCreateProgramWithSource(context, 1, &kernel_source, nullptr, &err);
    BOOST_CHECK_EQUAL(err, CL_SUCCESS);
    BOOST_CHECK(temp_program != nullptr);

    err = clBuildProgram(temp_program, 1, &device, nullptr, nullptr, nullptr);
    if (err != CL_SUCCESS)
    {
        size_t log_size;
        clGetProgramBuildInfo(
            temp_program, device, CL_PROGRAM_BUILD_LOG, 0, nullptr, &log_size);
        std::vector<char> log(log_size);
        clGetProgramBuildInfo(temp_program,
                              device,
                              CL_PROGRAM_BUILD_LOG,
                              log_size,
                              log.data(),
                              nullptr);
        std::cout << "Build log: " << log.data() << std::endl;
    }
    BOOST_CHECK_EQUAL(err, CL_SUCCESS);

    // 获取程序二进制大小
    size_t binary_size = 0;
    err                = clGetProgramInfo(temp_program,
                           CL_PROGRAM_BINARY_SIZES,
                           sizeof(size_t),
                           &binary_size,
                           nullptr);
    BOOST_CHECK_EQUAL(err, CL_SUCCESS);
    BOOST_CHECK_GT(binary_size, 0);

    // 获取程序二进制数据
    std::vector<unsigned char>  binary(binary_size);
    std::vector<unsigned char*> binary_pointers = {binary.data()};

    err = clGetProgramInfo(temp_program,
                           CL_PROGRAM_BINARIES,
                           sizeof(unsigned char*),
                           binary_pointers.data(),
                           nullptr);
    BOOST_CHECK_EQUAL(err, CL_SUCCESS);

    // 使用二进制数据创建新程序
    cl_program program_from_binary = clCreateProgramWithBinary(
        context,
        1,
        &device,
        &binary_size,
        const_cast<const unsigned char**>(binary_pointers.data()),
        nullptr,
        &err);
    BOOST_CHECK_EQUAL(err, CL_SUCCESS);
    BOOST_CHECK(program_from_binary != nullptr);

    // 构建从二进制创建的程序（通常不需要，但为了完整性测试）
    err = clBuildProgram(
        program_from_binary, 1, &device, nullptr, nullptr, nullptr);
    // 某些实现可能不需要重新构建从二进制创建的程序，所以不强求成功
    if (err != CL_SUCCESS)
    {
        size_t log_size;
        clGetProgramBuildInfo(program_from_binary,
                              device,
                              CL_PROGRAM_BUILD_LOG,
                              0,
                              nullptr,
                              &log_size);
        std::vector<char> log(log_size);
        clGetProgramBuildInfo(program_from_binary,
                              device,
                              CL_PROGRAM_BUILD_LOG,
                              log_size,
                              log.data(),
                              nullptr);
        std::cout << "Build log: " << log.data() << std::endl;
    }

    // 验证从二进制创建的程序
    size_t retrieved_binary_size = 0;
    err                          = clGetProgramInfo(program_from_binary,
                           CL_PROGRAM_BINARY_SIZES,
                           sizeof(size_t),
                           &retrieved_binary_size,
                           nullptr);
    BOOST_CHECK_EQUAL(err, CL_SUCCESS);
    BOOST_CHECK_GT(retrieved_binary_size, 0);

    // 清理程序
    err = clReleaseProgram(temp_program);
    BOOST_CHECK_EQUAL(err, CL_SUCCESS);

    err = clReleaseProgram(program_from_binary);
    BOOST_CHECK_EQUAL(err, CL_SUCCESS);

    cl.DeInit();
}

// 测试CLWrapper中的程序创建方法
BOOST_AUTO_TEST_CASE(TestCLWrapperCreateProgram)
{
    CLWrapper cl;
    bool      init_result = cl.Init();
    BOOST_CHECK(init_result == true);

    cl_context   context = cl.GetContext();
    cl_device_id device  = cl.GetDeviceID();
    BOOST_CHECK(context != nullptr);
    BOOST_CHECK(device != nullptr);

    // 测试CLWrapper的CreateProgram方法
    cl_program program =
        cl.CreateProgram(simple_kernel_source, strlen(simple_kernel_source));
    BOOST_CHECK(program != nullptr);

    // 验证程序对象
    cl_context program_context = nullptr;
    cl_int     err             = clGetProgramInfo(program,
                                  CL_PROGRAM_CONTEXT,
                                  sizeof(program_context),
                                  &program_context,
                                  nullptr);
    BOOST_CHECK_EQUAL(err, CL_SUCCESS);
    BOOST_CHECK(program_context == context);

    // 测试程序设备列表
    size_t devices_size = 0;
    err                 = clGetProgramInfo(
        program, CL_PROGRAM_DEVICES, 0, nullptr, &devices_size);
    if (err == CL_SUCCESS && devices_size > 0)
    {
        std::vector<cl_device_id> devices(devices_size / sizeof(cl_device_id));
        err = clGetProgramInfo(
            program, CL_PROGRAM_DEVICES, devices_size, devices.data(), nullptr);
        BOOST_CHECK_EQUAL(err, CL_SUCCESS);
    }

    cl.DeInit();
}

// 测试程序构建选项
BOOST_AUTO_TEST_CASE(TestProgramBuildWithOptions)
{
    CLWrapper cl;
    bool      init_result = cl.Init();
    BOOST_CHECK(init_result == true);

    cl_context   context = cl.GetContext();
    cl_device_id device  = cl.GetDeviceID();
    BOOST_CHECK(context != nullptr);
    BOOST_CHECK(device != nullptr);

    // 创建程序对象
    cl_int     err     = CL_SUCCESS;
    cl_program program = clCreateProgramWithSource(
        context, 1, &simple_kernel_source, nullptr, &err);
    BOOST_CHECK_EQUAL(err, CL_SUCCESS);
    BOOST_CHECK(program != nullptr);

    // 使用构建选项构建程序
    const char* build_options = "-cl-fast-relaxed-math";
    err = clBuildProgram(program, 1, &device, build_options, nullptr, nullptr);
    if (err != CL_SUCCESS)
    {
        // 如果构建失败，获取并打印构建日志
        size_t log_size = 0;
        clGetProgramBuildInfo(
            program, device, CL_PROGRAM_BUILD_LOG, 0, nullptr, &log_size);

        if (log_size > 1)
        {
            std::vector<char> build_log(log_size);
            clGetProgramBuildInfo(program,
                                  device,
                                  CL_PROGRAM_BUILD_LOG,
                                  log_size,
                                  build_log.data(),
                                  nullptr);
            std::cout << "Build log: " << build_log.data() << std::endl;
        }
    }
    BOOST_CHECK_EQUAL(err, CL_SUCCESS);

    // 验证构建状态
    cl_build_status build_status;
    err = clGetProgramBuildInfo(program,
                                device,
                                CL_PROGRAM_BUILD_STATUS,
                                sizeof(build_status),
                                &build_status,
                                nullptr);
    BOOST_CHECK_EQUAL(err, CL_SUCCESS);
    BOOST_CHECK_EQUAL(build_status, CL_BUILD_SUCCESS);

    // 清理
    err = clReleaseProgram(program);
    BOOST_CHECK_EQUAL(err, CL_SUCCESS);

    cl.DeInit();
}

// 测试程序信息查询
BOOST_AUTO_TEST_CASE(TestProgramInfo)
{
    CLWrapper cl;
    bool      init_result = cl.Init();
    BOOST_CHECK(init_result == true);

    cl_context   context = cl.GetContext();
    cl_device_id device  = cl.GetDeviceID();
    BOOST_CHECK(context != nullptr);
    BOOST_CHECK(device != nullptr);

    cl_int     err     = CL_SUCCESS;
    cl_program program = clCreateProgramWithSource(
        context, 1, &simple_kernel_source, nullptr, &err);
    BOOST_CHECK_EQUAL(err, CL_SUCCESS);
    BOOST_CHECK(program != nullptr);

    err = clBuildProgram(program, 1, &device, nullptr, nullptr, nullptr);
    if (err != CL_SUCCESS)
    {
        size_t log_size = 0;
        clGetProgramBuildInfo(
            program, device, CL_PROGRAM_BUILD_LOG, 0, nullptr, &log_size);
        if (log_size > 1)
        {
            std::vector<char> build_log(log_size);
            clGetProgramBuildInfo(program,
                                  device,
                                  CL_PROGRAM_BUILD_LOG,
                                  log_size,
                                  build_log.data(),
                                  nullptr);
            std::cout << "Build log: " << build_log.data() << std::endl;
        }
    }
    BOOST_CHECK_EQUAL(err, CL_SUCCESS);

    // 测试查询程序上下文
    cl_context program_context = nullptr;
    err                        = clGetProgramInfo(program,
                           CL_PROGRAM_CONTEXT,
                           sizeof(program_context),
                           &program_context,
                           nullptr);
    BOOST_CHECK_EQUAL(err, CL_SUCCESS);
    BOOST_CHECK(program_context != nullptr);

    // 测试查询程序设备数量
    cl_uint num_devices = 0;
    err                 = clGetProgramInfo(program,
                           CL_PROGRAM_NUM_DEVICES,
                           sizeof(num_devices),
                           &num_devices,
                           nullptr);
    BOOST_CHECK_EQUAL(err, CL_SUCCESS);
    BOOST_CHECK_GT(num_devices, 0);

    // 测试查询程序引用计数
    cl_uint ref_count = 0;
    err               = clGetProgramInfo(program,
                           CL_PROGRAM_REFERENCE_COUNT,
                           sizeof(ref_count),
                           &ref_count,
                           nullptr);
    BOOST_CHECK_EQUAL(err, CL_SUCCESS);
    BOOST_CHECK_GT(ref_count, 0);

    // 清理
    err = clReleaseProgram(program);
    BOOST_CHECK_EQUAL(err, CL_SUCCESS);

    cl.DeInit();
}

// 测试程序构建日志获取
BOOST_AUTO_TEST_CASE(TestProgramBuildLog)
{
    // 使用一个有语法错误的内核源码来测试构建日志
    const char* invalid_kernel_source =
        "__kernel void invalid_kernel() { invalid_function(); }";  // 无效函数调用

    CLWrapper cl;
    bool      init_result = cl.Init();
    BOOST_CHECK(init_result == true);

    cl_context   context = cl.GetContext();
    cl_device_id device  = cl.GetDeviceID();
    BOOST_CHECK(context != nullptr);
    BOOST_CHECK(device != nullptr);

    cl_int     err     = CL_SUCCESS;
    cl_program program = clCreateProgramWithSource(
        context, 1, &invalid_kernel_source, nullptr, &err);
    BOOST_CHECK_EQUAL(err, CL_SUCCESS);
    BOOST_CHECK(program != nullptr);

    // 尝试构建程序（这应该失败）
    err = clBuildProgram(program, 1, &device, nullptr, nullptr, nullptr);

    // 获取构建日志
    size_t log_size = 0;
    cl_int log_err  = clGetProgramBuildInfo(
        program, device, CL_PROGRAM_BUILD_LOG, 0, nullptr, &log_size);
    BOOST_CHECK_EQUAL(log_err, CL_SUCCESS);

    if (log_size > 1)
    {
        std::vector<char> build_log(log_size);
        log_err = clGetProgramBuildInfo(program,
                                        device,
                                        CL_PROGRAM_BUILD_LOG,
                                        log_size,
                                        build_log.data(),
                                        nullptr);
        BOOST_CHECK_EQUAL(log_err, CL_SUCCESS);

        // 构建失败时日志应该包含错误信息
        if (err != CL_SUCCESS)
        {
            BOOST_CHECK_GT(strlen(build_log.data()), 0);
        }
    }

    // 清理
    err = clReleaseProgram(program);
    BOOST_CHECK_EQUAL(err, CL_SUCCESS);

    cl.DeInit();
}

// 测试多个程序对象
BOOST_AUTO_TEST_CASE(TestMultiplePrograms)
{
    CLWrapper cl;
    bool      init_result = cl.Init();
    BOOST_CHECK(init_result == true);

    cl_context   context = cl.GetContext();
    cl_device_id device  = cl.GetDeviceID();
    BOOST_CHECK(context != nullptr);
    BOOST_CHECK(device != nullptr);

    const int               num_programs = 2;
    std::vector<cl_program> programs(num_programs);

    // 创建多个程序
    for (int i = 0; i < num_programs; i++)
    {
        const char* kernel_source =
            (i == 0) ? simple_kernel_source
                     : "__kernel void second_kernel(__global int* data) { "
                       "data[get_global_id(0)] *= 2; }";

        cl_int err  = CL_SUCCESS;
        programs[i] = clCreateProgramWithSource(
            context, 1, &kernel_source, nullptr, &err);
        BOOST_CHECK_EQUAL(err, CL_SUCCESS);
        BOOST_CHECK(programs[i] != nullptr);

        // 构建程序
        err =
            clBuildProgram(programs[i], 1, &device, nullptr, nullptr, nullptr);
        if (err != CL_SUCCESS)
        {
            size_t log_size = 0;
            clGetProgramBuildInfo(programs[i],
                                  device,
                                  CL_PROGRAM_BUILD_LOG,
                                  0,
                                  nullptr,
                                  &log_size);
            if (log_size > 1)
            {
                std::vector<char> build_log(log_size);
                clGetProgramBuildInfo(programs[i],
                                      device,
                                      CL_PROGRAM_BUILD_LOG,
                                      log_size,
                                      build_log.data(),
                                      nullptr);
                std::cout << "Build log: " << build_log.data() << std::endl;
            }
        }
        BOOST_CHECK_EQUAL(err, CL_SUCCESS);
    }

    // 验证每个程序
    for (int i = 0; i < num_programs; i++)
    {
        cl_context program_context = nullptr;
        cl_int     err             = clGetProgramInfo(programs[i],
                                      CL_PROGRAM_CONTEXT,
                                      sizeof(program_context),
                                      &program_context,
                                      nullptr);
        BOOST_CHECK_EQUAL(err, CL_SUCCESS);
        BOOST_CHECK(program_context == context);

        cl_uint ref_count = 0;
        err               = clGetProgramInfo(programs[i],
                               CL_PROGRAM_REFERENCE_COUNT,
                               sizeof(ref_count),
                               &ref_count,
                               nullptr);
        BOOST_CHECK_EQUAL(err, CL_SUCCESS);
        BOOST_CHECK_GT(ref_count, 0);
    }

    // 清理所有程序
    for (int i = 0; i < num_programs; i++)
    {
        cl_int err = clReleaseProgram(programs[i]);
        BOOST_CHECK_EQUAL(err, CL_SUCCESS);
    }

    cl.DeInit();
}
